package com.example.demo.retry;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.sun.javafx.fxml.BeanAdapter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 *重推工具类
 */
@Component
@DependsOn("springContextHolder")
@Slf4j
public class RetryUtil {


	public static String retryMethod(String clazzName, String methodName, String paramsObject){
		Object exeClazz;
		try {
			exeClazz = SpringContextHolder.getBean(Class.forName(clazzName));
			if(StringUtils.isBlank(paramsObject)){
				return noArgsExec(exeClazz,methodName);
			}
			return argsExec(exeClazz,methodName,paramsObject);
		} catch (Throwable e) {
			log.error("手动重试异常", e);
			return e.toString();
		}
	}

	public static String retryMethod(String clazzName,String methodName,String paramsObject,String threadLocalMap) {
		Object exeClazz;
		try {
			setThreadLocal(threadLocalMap);
			exeClazz = SpringContextHolder.getBean(Class.forName(clazzName));
			if(StringUtils.isBlank(paramsObject)){
				return noArgsExec(exeClazz,methodName);
			}
			return argsExec(exeClazz,methodName,paramsObject);
		} catch (Throwable e) {
			log.error("手动重试异常", e);
			return e.toString();
		}finally {
			removeThreadLocal(threadLocalMap);
		}
	}

	private static void setThreadLocal(String threadLocalMap) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
		if(StringUtils.isBlank(threadLocalMap)){
			return;
		}
		Map<String,Object> map = JSONUtil.toBean(threadLocalMap, Map.class);
		if(CollectionUtil.isEmpty(map)){
			return;
		}
		for (Map.Entry<String, Object> entries:map.entrySet()) {
			String[] key = entries.getKey().split("@");
			Object tlClazz = Class.forName(key[0]);
			Field field = tlClazz.getClass().getField(key[1]);
			Object fieldObject = field.get(null);
			Method method = fieldObject.getClass().getMethod("set",Object.class);
			method.invoke(fieldObject,entries.getValue());
		}
	}

	private static void removeThreadLocal(String threadLocalMap) {
		if(StringUtils.isBlank(threadLocalMap)){
			return;
		}
		Map<String,Object> map = JSONUtil.toBean(threadLocalMap, Map.class);
		if(CollectionUtil.isEmpty(map)){
			return;
		}
		for (Map.Entry<String, Object> entries:map.entrySet()) {
			try {
				String[] key = entries.getKey().split("@");
				Object tlClazz = Class.forName(key[0]);
				Field field = tlClazz.getClass().getField(key[1]);
				Object fieldObject = field.get(null);
				Method method = fieldObject.getClass().getMethod("remove");
				method.invoke(fieldObject);
			}catch (Exception e){

			}
		}
	}

	private static String argsExec(Object exeClazz, String methodName, String paramsObject) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		List<SysMethodRetryLogServiceImpl.Param> paramList = JSONUtil.toList(paramsObject, SysMethodRetryLogServiceImpl.Param.class);
		if(CollectionUtil.isEmpty(paramList)){
			return noArgsExec(exeClazz,methodName);
		}
		paramList.forEach(e->{
			try {
				int i = e.getType().indexOf("<");
				if(i==-1){
					e.setClazz(Class.forName(e.getType()));
					return;
				}
				e.setClazz(Class.forName(e.getType().substring(0,i)));
				e.setGenericClazz(Class.forName(e.getType().substring(i+1,e.getType().indexOf(">"))));
			} catch (ClassNotFoundException ex) {
			}
		});
		Class<?>[] paramClazz = getParamClazz(paramList);
		Object[] params = getParams(paramList);
		Method method = exeClazz.getClass().getMethod(methodName, paramClazz);
		Object objRet= method.invoke(exeClazz, params);
		return JSONUtil.toJsonStr(objRet);
	}

	public static String noArgsExec(Object exeClazz, String methodName) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
		Method method = exeClazz.getClass().getMethod(methodName);
		Object objRet= method.invoke(exeClazz);
		return JSONUtil.toJsonStr(objRet);
	}

	private static Object[] getParams(List<SysMethodRetryLogServiceImpl.Param> paramsObject) {
		if(CollectionUtil.isEmpty(paramsObject)){
			return null;
		}
		return paramsObject.stream().map(e->{
			if(e.getClazz().isPrimitive()||PRIMITIVE_TYPES.contains(e.getClazz())||e.getClazz().isEnum()){
				return getValueOf(e);
			}else if(Collection.class.isAssignableFrom(e.getClazz())){
				return JSONUtil.toList(e.getValue(),e.getGenericClazz());
			}
			return JSONUtil.toBean(e.getValue(),e.getClazz());
		}).collect(Collectors.toList()).toArray();
	}

	private static Object getValueOf(SysMethodRetryLogServiceImpl.Param e) {
		if(StringUtils.isBlank(e.getValue())){
			return null;
		}
		if(String.class.equals(e.getClazz())){
			return e.getValue();
		}
		Method method1 = null;
		try {
			method1 = e.getClazz().getMethod("valueOf",String.class);
			return method1.invoke(null,e.getValue());
		} catch (Exception ex) {
			log.error("对象不存在！",ex);
			return null;
		}
	}

	private static final List<Class> PRIMITIVE_TYPES = Arrays.asList(String.class,
			Integer.class,
			Long.class,
			Float.class,
			Double.class,
			BigDecimal.class);

	public static Class<?>[] getParamClazz(List<SysMethodRetryLogServiceImpl.Param> paramsObject) {
		if(CollectionUtil.isEmpty(paramsObject)){
			return null;
		}
		List<Class<?>> collect = paramsObject.stream().map(SysMethodRetryLogServiceImpl.Param::getClazz).collect(Collectors.toList());
		Class<?>[] classes = new Class<?>[paramsObject.size()];
		for (int i=0;i<paramsObject.size();i++){
			classes[i] = collect.get(i);
		}
		return classes;
	}

//	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//		Class<?> aClass2 = Class.forName("java.util.List");
//		Class<?> aClass = Class.forName("com.zt.mpf.mod.openapi.service.provider.utils.RetryUtil");
//		Method getParamClazz = aClass.getMethod("getParamClazz", List.class);
//		Class<?>[] parameterTypes = getParamClazz.getParameterTypes();
//		Type[] genericParameterTypes = getParamClazz.getGenericParameterTypes();
//		String typeName = genericParameterTypes[0].getTypeName();
//
//
//		System.out.println(typeName.substring(typeName.indexOf("<")+1,typeName.indexOf(">")));
//		System.out.println(Collection.class.isAssignableFrom(List.class));
//	}


	public static void main(String[] args) {

		try {
			KongAdminContext.KongEnv.set("1234");
			Class<?> clazz = Class.forName("com.zt.mpf.mod.openapi.service.kong.KongAdminContext");
			Field field = clazz.getField("KongEnv");
			Object fieldObject = field.get(null);
//			Object fieldObject = field.get(String.class);
			Method method = fieldObject.getClass().getMethod("set",Object.class);
			method.invoke(fieldObject,"2222");
			System.out.println(KongAdminContext.KongEnv.get());

//			System.out.println(KongAdminContext.KongEnv.get());
		} catch (Exception e) {
			e.printStackTrace();
		}


	}

}
